package flexUnitTests.testCases
{
	import com.competentum.builders.BindsBuilder;
	import com.competentum.builders.DataBuildersFactory;
	import com.competentum.builders.EmployeesBuilder;
	import com.competentum.builders.ProjectsBuilder;
	import com.competentum.controls.ADG;
	import com.competentum.controls.panel.CommandPanel;
	import com.competentum.engines.BindsEngine;
	import com.competentum.engines.CommandPanelEngine;
	import com.competentum.engines.EmployeesEngine;
	import com.competentum.engines.EngineFactory;
	import com.competentum.engines.EngineWithView;
	import com.competentum.engines.InputOutputEngine;
	import com.competentum.engines.ProjectsEngine;
	import com.competentum.engines.ShortCutsEngine;
	import com.competentum.events.BindEvent;
	import com.competentum.events.EventReasons;
	import com.competentum.events.GroupingEvent;
	import com.competentum.events.ItemMoveEvent;
	import com.competentum.events.WorkloadEvent;
	import com.competentum.history.HistoryChangesManager;
	import com.competentum.io.IEngineView;
	import com.competentum.ui.EasyWorkloadWindow;
	import com.competentum.vos.AbstractItemVO;
	import com.competentum.vos.BindVO;
	import com.competentum.vos.EmployeeVO;
	import com.competentum.vos.ProjectVO;
	import com.competentum.windows.ScopesManager;
	import com.competentum.lib.data.builders.AbstractDataBuilder;
	import com.competentum.lib.data.vo.UniqueVO;
	import com.competentum.lib.engines.AbstractEngine;
	import com.competentum.lib.events.CommandEvent;
	import com.competentum.lib.events.InputOutputDataEvent;
	
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	
	import flashx.textLayout.debug.assert;
	
	import mx.core.FlexGlobals;
	import mx.core.UIComponent;
	
	import org.flexunit.asserts.assertEquals;
	import org.fluint.uiImpersonation.UIImpersonator;

	public class HistoryTest
	{	
		private var dataBuildersFactory:DataBuildersFactory;
		private var engineFactory:EngineFactory;
		private var rootEngine:AbstractEngine;

		private var projectsBuilder:ProjectsBuilder;
		private var employeesBuilder:EmployeesBuilder;
		private var bindsBuilder:BindsBuilder;

		[Before]
		public function setUp():void
		{
			var window:EasyWorkloadWindow = new EasyWorkloadWindow();
			ScopesManager.addScope(window);
			configureEngines();
			configureListeners();
			setConfiguration();
		}
		
		[After]
		public function tearDown():void
		{
		}
		
		[BeforeClass]
		public static function setUpBeforeClass():void
		{
		}
		
		private function configureEngines():void
		{
			dataBuildersFactory = new DataBuildersFactory();
			engineFactory = new EngineFactory();
			
			var employeesEngine:EmployeesEngine = engineFactory.getEngine(EmployeesEngine.TYPE) as EmployeesEngine;
			var employeesTable:IEngineView = new ADG();
			employeesTable.scope = ScopesManager.currentScope;
			employeesEngine.addView(employeesTable);
			rootEngine = employeesEngine;
			
			var projectsEngine:ProjectsEngine = engineFactory.getEngine(ProjectsEngine.TYPE) as ProjectsEngine;
			var projectsTable:IEngineView = new ADG();
			projectsTable.scope = ScopesManager.currentScope;
			projectsEngine.addView(projectsTable);
			rootEngine.addEngine(projectsEngine);
			
			var bindsEngine:AbstractEngine = engineFactory.getEngine(BindsEngine.TYPE);
			rootEngine.addEngine(bindsEngine);
			
			var commandPanelEngine:EngineWithView = engineFactory.getEngine(CommandPanelEngine.TYPE) as EngineWithView;
			var commandPanel:IEngineView = new CommandPanel();
			commandPanel.scope = ScopesManager.currentScope;
			commandPanelEngine.addView(commandPanel);
			rootEngine.addEngine(commandPanelEngine);
			
			var ioEngine:AbstractEngine = engineFactory.getEngine(InputOutputEngine.TYPE);
			rootEngine.addEngine(ioEngine);
			
			/*var shortCutsEngine:ShortCutsEngine = engineFactory.getEngine(ShortCutsEngine.TYPE) as ShortCutsEngine;
			var stageObject:UIComponent = new UIComponent();
			UIImpersonator.addChild(stageObject);
			shortCutsEngine.window = stageObject;
			rootEngine.addEngine(shortCutsEngine);*/
		}
		
		private function configureListeners():void
		{
			ScopesManager.currentWindow.addEventListener(CommandEvent.ACTIVATE_ENGINE, onCommandEventHandler);
			
			if (rootEngine)
			{
				rootEngine.addChainListener(onCommandEventHandler);
			}
		}
		
		private function onCommandEventHandler(event:CommandEvent):void
		{
			rootEngine.handle(event);
		}
		
		private function setConfiguration():void
		{
			projectsBuilder = DataBuildersFactory.getDataBuilder(ProjectsBuilder.TYPE, ScopesManager.currentScope) as ProjectsBuilder;
			employeesBuilder = DataBuildersFactory.getDataBuilder(EmployeesBuilder.TYPE, ScopesManager.currentScope) as EmployeesBuilder;
			bindsBuilder = DataBuildersFactory.getDataBuilder(BindsBuilder.TYPE, ScopesManager.currentScope) as BindsBuilder;
		}
		
		[AfterClass]
		public static function tearDownAfterClass():void
		{
		}
		
		[Test]
		public function test():void
		{
			createEmployee();
			createEmployee();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			createEmployee();
			createEmployee();
			createEmployee();
			createProject();
			ScopesManager.currentScope.historyManager.undo();
			createProject();
			createProject();
			
			// EventReasons.ADD employee, project
			assertEquals(3, employeesBuilder.itemsCount);
			assertEquals(2, projectsBuilder.itemsCount);
			
			const EMPLOYEE:String = 'employee';
			const PROJECT:String = 'project';
			const VECTOR:String = 'vector';
			const SPB:String = 'spb';
			const NO_NAME:String = 'noName';
			const TEAM:String = 'team';
			const GROUP:String = 'group';
			const COMMENTS:String = 'Comments';
			// EventReasons.UPDATE employee
			updateEmployee(0, EMPLOYEE, VECTOR, 40);
			updateEmployee(1, EMPLOYEE+1, TEAM, 50);
			updateEmployee(2, EMPLOYEE+2, TEAM, 60);
			
			var employee:EmployeeVO = getEmployee(2);
			compareAll([[employee.name, EMPLOYEE+2], [employee.team, TEAM], [employee.hours, 60]]);
			
			ScopesManager.currentScope.historyManager.undo();

			employee = getEmployee(2);
			compareAll([[employee.name, NO_NAME], [employee.team, TEAM], [employee.hours, 0]]);
			
			updateEmployee(2, EMPLOYEE+2, VECTOR, 70);
			createEmployee();

			employee = getEmployee(0);
			compareAll([[employee.name, EMPLOYEE], [employee.team, VECTOR], [employee.hours, 40]]);
			employee = getEmployee(1);
			compareAll([[employee.name, EMPLOYEE+1], [employee.team, TEAM], [employee.hours, 50]]);
			employee = getEmployee(2);
			compareAll([[employee.name, EMPLOYEE+2], [employee.team, VECTOR], [employee.hours, 70]]);
			employee = getEmployee(3);
			compareAll([[employee.name, NO_NAME], [employee.team, TEAM], [employee.hours, 0]]);
			
			updateProject(0, 'fao', SPB, 100, COMMENTS);
			ScopesManager.currentScope.historyManager.undo();
			var project:ProjectVO = getProject(0);
			compareAll([[project.name, NO_NAME], [project.groupingAttribute, GROUP], [project.hours, 0], [project.comments, null]]);

			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();

			// EventReasons.UPDATE project
			updateProject(0, PROJECT, SPB, 200, COMMENTS);
			updateProject(1, PROJECT, SPB, 300, COMMENTS+1);
			createProject();
			updateProject(2, PROJECT, GROUP+1, 500, 'fjjkjff');
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			createProject();
			updateProject(2, PROJECT+1, GROUP, 400, 'fff');
			
			project = getProject(0);
			compareAll([[project.name, PROJECT], [project.groupingAttribute, SPB], [project.hours, 200], [project.comments, COMMENTS]]);
			project = getProject(1);
			compareAll([[project.name, PROJECT], [project.groupingAttribute, SPB], [project.hours, 300], [project.comments, COMMENTS+1]]);
			project = getProject(2);
			compareAll([[project.name, PROJECT+1], [project.groupingAttribute, GROUP], [project.hours, 400], [project.comments, 'fff']]);
			
			// EventReasons.ADD bind
			createBind(0, 1, 1);
			createBind(2, 2, 7);
			ScopesManager.currentScope.historyManager.undo();
			createBind(0, 2, 2);
			createBind(1, 0, 3);
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			createBind(1, 1, 10);
			createBind(1, 1, 4);
			createBind(3, 2, 5);
			
			assertEquals(3, bindsBuilder.getTotalReservedHours(getEmployee(0)));
			assertEquals(7, bindsBuilder.getTotalReservedHours(getEmployee(1)));
			assertEquals(0, bindsBuilder.getTotalReservedHours(getEmployee(2)));
			assertEquals(5, bindsBuilder.getTotalReservedHours(getEmployee(3)));
			assertEquals(3, bindsBuilder.getTotalReservedHours(getProject(0)));
			assertEquals(5, bindsBuilder.getTotalReservedHours(getProject(1)));
			assertEquals(7, bindsBuilder.getTotalReservedHours(getProject(2)));
			assertEquals(2, bindsBuilder.getBindingsListForItem(getEmployee(1)).length);
			var bindingsList:Vector.<BindVO> = bindsBuilder.getBindingsListForItem(getProject(1));
			compareAll([[bindingsList[0], getBind(0)], [bindingsList[1], getBind(3)]]);
			bindingsList = bindsBuilder.getBindingsListForItem(getEmployee(3));
			assertEquals(bindingsList[0], getBind(4));
			
			// EventReasons.MOVE employee
			moveEmployee(2, TEAM);
			ScopesManager.currentScope.historyManager.undo();
			moveEmployee(0, TEAM);
			employee = getEmployee(0);
			assertEquals(employee.team, TEAM);
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			employee = getEmployee(2);
			assertEquals(employee.team, VECTOR);
			
			// EventReasons.MOVE project
			moveProject(0, GROUP);
			ScopesManager.currentScope.historyManager.undo();
			moveProject(1, GROUP);
			project = getProject(0);
			assertEquals(project.groupingAttribute, SPB);
			project = getProject(2);
			assertEquals(project.groupingAttribute, GROUP);
			
			// EventReasons.MOVE bind
			var binds:Vector.<BindVO> = new Vector.<BindVO>();
			for (var i:int = 0; i < bindsBuilder.itemsCount; i ++ )
			{
				binds.push(getBind(i));
			}
			moveBind(binds[2], 1);
			ScopesManager.currentScope.historyManager.undo();
			moveBind(binds[4], 1);
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			moveBind(binds[1], 1);
			var bind:BindVO = binds[0];
			assertEquals(bind.employee, getEmployee(0));
			assertEquals(bind.project, getProject(1));
			assertEquals(bind.hoursReserved, 2);
			bind = binds[2];
			assertEquals(bind.employee, getEmployee(1));
			assertEquals(bind.project, getProject(0));
			assertEquals(bind.hoursReserved, 3);
			bind = binds[3];
			assertEquals(bind.employee, getEmployee(1));
			assertEquals(bind.project, getProject(1));
			assertEquals(bind.hoursReserved, 4);
			bind = binds[4];
			assertEquals(bind.employee, getEmployee(3));
			assertEquals(bind.project, getProject(1));
			assertEquals(bind.hoursReserved, 5);
			// check that bind-1 was removed from builder
			assertEquals(isInBuilder(bindsBuilder, binds[1]), false);
			
			// check employees' and projects' reserved hours
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(0)), 2);
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(1)), 7);
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(2)), 0);
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(3)), 5);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(0)), 3);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(1)), 11);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(2)), 0);
			
			// EventReasons.UPDATE bind
			updateBind(binds[3], 10);
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(1)), 13);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(1)), 17);
			
			// EventReasons.REMOVE all
			removeBind(binds[3]);
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(1)), 3);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(1)), 7);
			ScopesManager.currentScope.historyManager.undo();
			assertEquals(bindsBuilder.getTotalReservedHours(getEmployee(1)), 13);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(1)), 17);
			
			var employees:Vector.<EmployeeVO> = new Vector.<EmployeeVO>();
			for (i = 0; i < employeesBuilder.itemsCount; i ++ )
			{
				employees.push(getEmployee(i));
			}
			removeEmployee(employees[1]);
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(0)), 0);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(1)), 7);
			assertEquals(isInBuilder(bindsBuilder, binds[2]), false);
			assertEquals(isInBuilder(bindsBuilder, binds[3]), false);
			assertEquals(isInBuilder(employeesBuilder, employees[1]), false);
			ScopesManager.currentScope.historyManager.undo();
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(0)), 3);
			assertEquals(bindsBuilder.getTotalReservedHours(getProject(1)), 17);
			assertEquals(isInBuilder(bindsBuilder, binds[2]), true);
			assertEquals(isInBuilder(bindsBuilder, binds[3]), true);
			assertEquals(isInBuilder(employeesBuilder, employees[1]), true);
			
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.undo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			ScopesManager.currentScope.historyManager.redo();
			
			var projects:Vector.<ProjectVO> = new Vector.<ProjectVO>();
			for (i = 0; i < projectsBuilder.itemsCount; i ++ )
			{
				projects.push(getProject(i));
			}
			removeProject(projects[1]);
			assertEquals(bindsBuilder.getTotalReservedHours(employees[0]), 0);
			assertEquals(bindsBuilder.getTotalReservedHours(employees[1]), 3);
			assertEquals(bindsBuilder.getTotalReservedHours(employees[3]), 0);
			assertEquals(isInBuilder(bindsBuilder, binds[0]), false);
			assertEquals(isInBuilder(bindsBuilder, binds[3]), false);
			assertEquals(isInBuilder(bindsBuilder, binds[4]), false);
			assertEquals(isInBuilder(employeesBuilder, projects[1]), false);
			ScopesManager.currentScope.historyManager.undo();
			assertEquals(bindsBuilder.getTotalReservedHours(employees[0]), 2);
			assertEquals(bindsBuilder.getTotalReservedHours(employees[1]), 13);
			assertEquals(bindsBuilder.getTotalReservedHours(employees[3]), 5);
			assertEquals(isInBuilder(bindsBuilder, binds[0]), true);
			assertEquals(isInBuilder(bindsBuilder, binds[3]), true);
			assertEquals(isInBuilder(bindsBuilder, binds[4]), true);
			assertEquals(isInBuilder(projectsBuilder, projects[1]), true);
		}
		
		private function getEmployee(index:int):EmployeeVO
		{
			return employeesBuilder.getItemByIndex(index) as EmployeeVO;
		}
		
		private function getProject(index:int):ProjectVO
		{
			return projectsBuilder.getItemByIndex(index) as ProjectVO;
		}
		
		private function getBind(index:int):BindVO
		{
			return bindsBuilder.getItemByIndex(index) as BindVO;
		}
		
		private function createEmployee():void
		{
			var grEvent:GroupingEvent = new GroupingEvent(CommandEvent.ACTIVATE_ENGINE, true);
			grEvent.reason = EventReasons.ADD;
			grEvent.commandType = EmployeesEngine.TYPE;
			var item:AbstractItemVO = new AbstractItemVO();
			item.name = 'team';
			grEvent.valueItem = item;
			grEvent.groupingItem = null;
			ScopesManager.currentWindow.dispatchEvent(grEvent);
		}

		private function createProject():void
		{
			var grEvent:GroupingEvent = new GroupingEvent(CommandEvent.ACTIVATE_ENGINE, true);
			grEvent.reason = EventReasons.ADD;
			grEvent.commandType = ProjectsEngine.TYPE;
			var item:AbstractItemVO = new AbstractItemVO();
			item.name = 'group';
			grEvent.valueItem = item;
			grEvent.groupingItem = null;
			ScopesManager.currentWindow.dispatchEvent(grEvent);
		}
		
		private function createBind(employee:int, project:int, hours:Number):void
		{
			var wEvent:BindEvent = new BindEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = BindsEngine.TYPE;
			wEvent.reason = EventReasons.ADD;
			wEvent.item = getEmployee(employee);
			wEvent.valueItem = getProject(project);
			(wEvent as BindEvent).hours = hours;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}
	
		private function updateEmployee(index:int, name:String, team:String, hours:Number):void
		{
			var valueItem:AbstractItemVO = employeesBuilder.createItem(name, team, hours, null, false);
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = EmployeesEngine.TYPE;
			wEvent.reason = EventReasons.UPDATE;
			wEvent.item = employeesBuilder.getItemByIndex(index) as AbstractItemVO;
			wEvent.valueItem = valueItem;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}
		
		private function updateProject(index:int, name:String, group:String, hours:Number, comments:String):void
		{
			var valueItem:AbstractItemVO = projectsBuilder.createItem(name, group, hours, comments, null, false);
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = ProjectsEngine.TYPE;
			wEvent.reason = EventReasons.UPDATE;
			wEvent.item = projectsBuilder.getItemByIndex(index) as AbstractItemVO;
			wEvent.valueItem = valueItem;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}
		
		private function updateBind(bind:BindVO, hours:Number):void
		{
			var valueItem:BindVO = bindsBuilder.createItem(null, null, hours, false) as BindVO;
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = BindsEngine.TYPE;
			wEvent.reason = EventReasons.UPDATE;
			wEvent.item = bind;
			wEvent.valueItem = valueItem;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}
		
		private function moveEmployee(index:int, team:String):void
		{
			var imEvent:ItemMoveEvent = new ItemMoveEvent(CommandEvent.ACTIVATE_ENGINE, true);
			imEvent.commandType = EmployeesEngine.TYPE;
			imEvent.reason = EventReasons.MOVE;
			imEvent.item = getEmployee(index);
			imEvent.group = team;
			ScopesManager.currentWindow.dispatchEvent(imEvent);
		}
		
		private function moveProject(index:int, group:String):void
		{
			var imEvent:ItemMoveEvent = new ItemMoveEvent(CommandEvent.ACTIVATE_ENGINE, true);
			imEvent.commandType = ProjectsEngine.TYPE;
			imEvent.reason = EventReasons.MOVE;
			imEvent.item = getProject(index);
			imEvent.group = group;
			ScopesManager.currentWindow.dispatchEvent(imEvent);
		}
		
		private function moveBind(bind:BindVO, project:int):void
		{
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = BindsEngine.TYPE;
			wEvent.item = bind;
			wEvent.valueItem = getProject(project);
			wEvent.reason = EventReasons.MOVE;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}
		
		private function removeEmployee(employee:EmployeeVO):void
		{
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = EmployeesEngine.TYPE;
			wEvent.reason = EventReasons.REMOVE;
			wEvent.item = employee;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}

		private function removeProject(project:ProjectVO):void
		{
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = ProjectsEngine.TYPE;
			wEvent.reason = EventReasons.REMOVE;
			wEvent.item = project;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}
		
		private function removeBind(bind:BindVO):void
		{
			var wEvent:WorkloadEvent = new WorkloadEvent(CommandEvent.ACTIVATE_ENGINE, true);
			wEvent.commandType = BindsEngine.TYPE;
			wEvent.reason = EventReasons.REMOVE;
			wEvent.item = bind;
			ScopesManager.currentWindow.dispatchEvent(wEvent);
		}

		private function compareAll(array:Array):void
		{
			for (var i:int = 0; i < array.length; i ++ )
			{
				assertEquals(array[i][0], array[i][1]);
			}
		}
		
		private function isInBuilder(builder:AbstractDataBuilder, item:UniqueVO):Boolean
		{
			var inItems:Boolean = false;
			for (var i:int = 0; i < builder.itemsCount; i ++ )
			{
				if (item == builder.getItemByIndex(i))
				{
					inItems = true;
				}
			}
			return inItems;
		}
	}
}